package com.qike.duola.config;

/**
 * @Author: Eric.zhou
 * @Date: 2020/10/16 15:01
 */

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Objects;

import javax.servlet.http.HttpServletRequest;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.qike.duola.domain.DuolaLog;
import com.qike.duola.domain.DuolaLogWithBLOBs;
import com.qike.duola.enums.LogTypeEnums;
import com.qike.duola.service.DuolaLogsService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * 切面类，日志
 *
 * @author luolei
 * @date 2019年2月11日
 */
@Aspect //声明为切面类
@Component
public class AspectLog {

    @Autowired
    private DuolaLogsService duolaLogsService;

    //定义切点表达式：*，第一个返回值，第二个类名，第三个方法名
    @Pointcut("execution(public * com.qike.duola.controller.*.*(..))")
    //使用一个返回值为void，方法体为空的方法来命名切入点
    public void myPointCut() {
    }

    ;

    private final Logger logger = LoggerFactory.getLogger(getClass());

//    //前置通知
//    @Before("myPointCut()")
//    public void myBefore(JoinPoint joinPoint) {
//        logger.info("-----------@Before 前置通知-----------");
////        logger.info("前置通知：模拟执行权限检查...");
//        logger.info("目标类是={}", joinPoint.getTarget());
//        logger.info("被植入增强目标为={}", joinPoint.getSignature());
//
//        ServletRequestAttributes attributes= (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
//        if (attributes!=null){
//        HttpServletRequest request =attributes.getRequest();
//            //url
//            logger.info("请求路径URL={}",request.getRequestURL());
//            //method
//            logger.info("请求方式Method={}",request.getMethod());
//            //ip
//            logger.info("请求IP={}",request.getRemoteAddr());
//            //类方法
//            logger.info("请求类方法ClassMethod={}",joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName());
//            //参数
//            logger.info("请求参数Args={}",joinPoint.getArgs());
////            DuolaLog duolaLog = new DuolaLog();
////            duolaLog.setAddTime(LocalDateTime.now());
////            duolaLog.setArgs(joinPoint.getArgs().toString());
////            duolaLog.setClassMethod(joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName());
////            duolaLog.setIp(request.getRemoteAddr());
////            duolaLog.setMethod(request.getMethod());
////            duolaLog.setType(LogTypeEnums.IN.getEcode());
////            duolaLogsService.saveLog(duolaLog);
//        }
//
//
//    }
//
//    //后置通知
///*	@AfterReturning("myPointCut()")
//	public void myAfterReturning(JoinPoint joinPoint) {
//		logger.info("-----------@AfterReturning 后置通知-----------");
//		logger.info("后置通知：模拟记录日志...");
//		logger.info("返回值 response={}", joinPoint.toString());
//	}*/
//
//    //后置通知
//    @AfterReturning(returning = "objects", pointcut = "myPointCut()")
//    public void myAfterReturning(Object objects) {
//        logger.info("-----------@AfterReturning 后置通知-----------");
//        logger.info("后置通知：模拟记录日志...");
//        logger.info("返回值 response={}", objects);
//    }

    /**
     * 环绕通知
     *
     * @param proceedingJoinPoint 是JoinPoint的子接口，表示可以执行目标方法
     * @return Object
     * 必须接收一个参数，类型为ProceedingJoinPoint
     * @必须 throws Throwable
     */
    @Around("myPointCut()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint)
            throws Throwable {
        //开始
        logger.info("-----------@Around 环绕通知start-----------");
        logger.info("环绕开始：开启之前，模拟开启事务...");
        long time = System.currentTimeMillis();
        //执行当前目标方法
        Object proceed = proceedingJoinPoint.proceed();
        //结束
        logger.info("环绕结束：模拟关闭事务...");
        time = System.currentTimeMillis() - time;
        logger.info("方法用时Time={}", time + "（毫秒）");
        logger.info("-----------@Around 环绕通知end-----------");
        DuolaLogWithBLOBs duolaLog = new DuolaLogWithBLOBs();
        duolaLog.setAddTime(LocalDateTime.now());
        duolaLog.setAddTime(LocalDateTime.now(ZoneOffset.UTC));

        duolaLog.setClassMethod(proceedingJoinPoint.getSignature().getDeclaringTypeName() + "." + proceedingJoinPoint.getSignature().getName());
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            duolaLog.setIp(request.getRemoteAddr());
            duolaLog.setMethod(request.getMethod());
            if (proceedingJoinPoint.getArgs().length>0){
                duolaLog.setArgs(JSONObject.toJSONString(proceedingJoinPoint.getArgs()[0]));
            }
//            Object result = proceedingJoinPoint.proceed();
            String respParam = postHandle(proceed);
            duolaLog.setTime(time);
            duolaLog.setReturns(respParam);
            duolaLog.setType(LogTypeEnums.IN.getEcode());
            duolaLogsService.saveLog(duolaLog);
        }
        return proceed;
    }

    //异常通知
//    @AfterThrowing(value = "myPointCut()", throwing = "e")
//    public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
//        logger.info("-----------@AfterThrowing 异常通知-----------");
//        logger.info("异常通知：出错了" + e);
//        logger.info("异常通知：出错了getStackTrace={}", e.getStackTrace());
//        logger.info("异常通知：出错了Throwable...具体如下", e);
//        DuolaLogWithBLOBs duolaLog = new DuolaLogWithBLOBs();
//        duolaLog.setAddTime(LocalDateTime.now());
////        duolaLog.setArgs(joinPoint.getArgs().toString());
//        duolaLog.setArgs(JSON.toJSONString(joinPoint.getArgs()));
//        duolaLog.setClassMethod(joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
//        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        if (attributes != null) {
//            HttpServletRequest request = attributes.getRequest();
//            duolaLog.setIp(request.getRemoteAddr());
//            duolaLog.setMethod(request.getMethod());
//        }
//        duolaLog.setReturns(e.getMessage());
//        duolaLog.setType(LogTypeEnums.ERROR.getEcode());
//        duolaLogsService.saveLog(duolaLog);
//    }

//    //最终通知
//    @After("myPointCut()")
//    public void myAfter() {
//        logger.info("-----------@After 最终通知-----------");
//        logger.info("最终通知：模拟方法结束后的释放资源...");
//    }

    /**
     * 返回数据
     *
     * @param retVal
     * @return
     */
    private String postHandle(Object retVal) {
        if (null == retVal) {
            return "";
        }
        return JSON.toJSONString(retVal);
    }

    /**
     * 入参数据
     *
     * @param joinPoint
     * @param request
     * @return
     */
    private String preHandle(ProceedingJoinPoint joinPoint, HttpServletRequest request) {

        String reqParam = "";
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        Annotation[] annotations = targetMethod.getAnnotations();
        for (Annotation annotation : annotations) {
            //此处可以改成自定义的注解
//            if (annotation.annotationType().equals(RequestMapping.class)) {
                reqParam = JSON.toJSONString(request.getParameterMap());
//                break;
//            }
        }
        return reqParam;
    }


}